package com.my.study.reflection;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author: yidujun
 * @create: 2021/07/16 11:18
 */
@Slf4j
public abstract class VFS {

    protected static final Class<?>[] IMPLEMENTATIONS = {DefaultVFS.class};

    protected static final List<Class<? extends VFS>> USER_IMPLEMENTATIONS = new ArrayList<>();

    private static class VFSHolder {
        static final VFS INSTANCE = createVFS();

        @SuppressWarnings("unchecked")
        static VFS createVFS() {
            List<Class<? extends VFS>> impls = new ArrayList<>();
            impls.addAll(USER_IMPLEMENTATIONS);
            impls.addAll(Arrays.asList((Class<? extends VFS>[]) IMPLEMENTATIONS));

            VFS vfs = null;

            for (int i = 0; vfs == null || !vfs.isValid(); i++) {
                Class<? extends VFS> impl = impls.get(i);
                try {
                    vfs = impl.getDeclaredConstructor().newInstance();
                } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
                    log.error("Failed to instantiate " + impl, e);
                    return null;
                }
            }

            return vfs;
        }
    }

    public static VFS getInstance() {
        return VFSHolder.INSTANCE;
    }

    public static void addImplClass(Class<? extends VFS> clazz) {
        if (null != clazz) {
            USER_IMPLEMENTATIONS.add(clazz);
        }
    }

    protected static List<URL> getResources(String path) throws IOException {
        return Collections.list(Thread.currentThread().getContextClassLoader().getResources(path));
    }

    /**
     * Return true if the {@link VFS} implementation is valid for the current environment
     *
     * @return true if the {@link VFS} implementation is valid for the current environment
     */
    public abstract boolean isValid();

    /**
     * 递归列出所有资源的完整资源路径，这些资源是由 URL 标识的资源的子资源。
     *
     * @param url
     * @param forPath
     * @return A list containing the names of the child resources
     * @throws IOException If I/O errors occur
     */
    public abstract List<String> list(URL url, String forPath) throws IOException;

    /**
     * 递归列出所有资源的完整资源路径，这些资源是在指定路径找到的所有资源的子资源
     *
     * @param path 要列出的资源的路径
     * @return 包含子资源名称的列表
     * @throws IOException If I/O errors occur
     */
    public List<String> list(String path) throws IOException {
        List<String> names = new ArrayList<>();
        List<URL> resources = getResources(path);
        for (URL url : resources) {
            names.addAll(list(url, path));
        }
        return names;
    }

}
